package core;

import branch.Branch;
import fileoperation.FileCreation;
import fileoperation.FileDeletion;
import fileoperation.FileStatus;
import gitobject.Blob;
import gitobject.Commit;
import gitobject.GitObject;
import gitobject.Tree;
import repository.Repository;
import utils.setOperation;
import stage.Index;

import java.io.*;
import java.util.*;

public class JitReset {
    final static String NO_COMMIT = "no_commit_yet";
    final static String GIT_DIR = Repository.getGitDir();

    public static void softReset(String version) throws IOException {
        String nextCommitHash = findCurrCommitHash(version);
        if(!nextCommitHash.equals(NO_COMMIT)){
            headRePoint(nextCommitHash);
        }
    }

    public static void mixedReset(String version) throws IOException {
        String nextCommitHash = findCurrCommitHash(version);
        if(nextCommitHash == null) return;

        HashMap<String,String> nextFiles = null;
        if(nextCommitHash.equals(NO_COMMIT)){
            nextFiles = new HashMap<String,String>();
        }else{
            String treePath = Commit.deserialize(nextCommitHash).getTree();

            //HashMap 存放 Hash:String,FilePath:String
            nextFiles = getAllBlob(treePath);
            //指针指向更改
            headRePoint(nextCommitHash);
        }

        //index 替换
        indexOverWrite(nextFiles);

        System.out.println(); //print nothing
    }

    public static void hardReset(String version) throws IOException {
        //查找到回滚的commit为nextCommitHash
        String nextCommitHash = findCurrCommitHash(version);
        if(nextCommitHash == null) return;

        HashMap<String,String> nextFiles = null;
        HashMap<String,String> currFiles = null;
        Commit nextCommit = null;

        if(nextCommitHash.equals(NO_COMMIT)){
            nextFiles = new HashMap<String,String>();
        }else{
            //将所有的文件信息读入hashMap中
            nextCommit = Commit.deserialize(nextCommitHash);
            String treePath = nextCommit.getTree(); // commit指向的tree的hash
            //HashMap 存放 Hash:String,FilePath:String
            nextFiles = getAllBlob(treePath);
        }
        currFiles = Index.getIndexMap();

        // HashSet 存放Hash:String
        HashSet<String> nextFilesKeySet  = setOperation.getDifference(nextFiles,currFiles);//add
        HashSet<String> currFilesKeySet = setOperation.getDifference(currFiles,nextFiles);//del

        //指针指向更改
        if(!nextCommitHash.equals(NO_COMMIT)){
            headRePoint(nextCommitHash);
            System.out.printf("HEAD is now at %s %s\n",nextCommitHash.substring(0,7), nextCommit.getMessage());
        }
        //index 替换
        indexOverWrite(nextFiles);
        //工作区更新
        WorkTreeModify(nextFilesKeySet,nextFiles,currFilesKeySet,currFiles);

    }

    //找到要回滚的commit文件的hash值
    private static String findCurrCommitHash(String version) throws IOException{
        Scanner input = new Scanner(new File(GIT_DIR + File.separator + "HEAD"));
        //refs\heads\master
        String masterPath = input.nextLine().trim().substring(5).replace("/", File.separator); // read last commit ID from file
        //.git\refs\heads\master
        File branchFile = new File(GIT_DIR + File.separator + masterPath);
        if(!branchFile.exists()){ //没有commit提交，没有.jit\refs\heads\master这个文件
            return NO_COMMIT;
        }
        String nextCommitHash = GitObject.getValue(branchFile);
        if(version.matches("^(?i)head(?:\\^*|~\\d*)")) {
            int versionsBack = 0;
            if(version.matches("^(?i)head\\^*")){
                versionsBack = version.length() - 4;
            }else{
                String tempVersionHolder = version.substring(version.length()-1);
                versionsBack = tempVersionHolder == "~" ? 0 : Integer.valueOf(tempVersionHolder);
            }
            for(int count = 0; count < versionsBack; count++){
                Commit currentCommit = Commit.deserialize(nextCommitHash);
                nextCommitHash = currentCommit.getParent();
                if(nextCommitHash == ""){//没有找到指定索引
                    System.out.printf("fatal: ambiguous argument %s: unknown revision or path not in the working tree.\n", version);
                    return null;
                }
            }
            return nextCommitHash;
        }else{//用户输入的commit 为某个hash值

            while(!nextCommitHash.startsWith(version)){
                Commit currentCommit = Commit.deserialize(nextCommitHash);
                nextCommitHash = currentCommit.getParent();
                if(nextCommitHash.equals("")) {
                    break; //说明不在以前的提交中
                }
            }
            if(!nextCommitHash.equals("")) return nextCommitHash;
            // hash向后查找
            String objectsPath = GIT_DIR+File.separator+"objects";
            List<File> gitObjectsList = FileStatus.objectsList(objectsPath);
            for (File file : gitObjectsList){
                String objPath = file.toString().replace(objectsPath,"").replace(File.separator,"");
                if(objPath.startsWith(version)){
                    if(GitObject.deserialize(objPath) instanceof Commit) return objPath;
                }
            }
            System.out.printf("fatal: ambiguous argument %s: unknown revision or path not in the working tree.\n", version);
            return null;
        }
    }


    //head指向更新
    static void headRePoint(String nextCommitHash) throws IOException {
        FileWriter commitWriter = null;
        branch.Branch curBranch = JitBranch.getCurBranch();
        try {
            commitWriter = new FileWriter(GIT_DIR + File.separator+ "refs" + File.separator + "heads"
                    + File.separator + curBranch.branchName);
        } catch (IOException e) {
            e.printStackTrace();
        }
        BufferedWriter out = new BufferedWriter(commitWriter);
        out.write(nextCommitHash); // Record the hash value of the latest commit.
        out.close();
        commitWriter.close();
    }

    //index更新
    static void indexOverWrite(HashMap<String, String> nextFiles) throws IOException {
        if(new File(GIT_DIR+File.separator+"index").exists()){
            File indexFile = new File(GIT_DIR + File.separator + "index");
            FileWriter fw = new FileWriter(indexFile);
            for(Map.Entry<String, String> entry : nextFiles.entrySet()){
                fw.write("100644" + " " + entry.getKey() + " " + entry.getValue() + " " + System.currentTimeMillis());
                fw.write("\n");
            }
            fw.flush();
            fw.close();
        }
    }

    //工作区del&add
    static void WorkTreeModify(
            HashSet<String> nextFilesKeySet,
            HashMap<String, String> nextFiles,
            HashSet<String> currFilesKeySet,
            HashMap<String, String> currFiles) throws IOException {

        String workDir = Repository.getWorkTree();

        for(String del : currFilesKeySet){//del
            FileDeletion.deleteFile(workDir+File.separator+currFiles.get(del));
        }

        for(String add : nextFilesKeySet){//add
            String relativeFilePath = nextFiles.get(add);
            //如果是多级目录 找到最后一个 /
            Blob b  = Blob.deserialize(add);
            int idx = relativeFilePath.lastIndexOf(File.separator);
            if(idx != -1){
                FileCreation.createFile(workDir + File.separator + relativeFilePath.substring(0, idx),
                        relativeFilePath.substring(idx+1), b.getValue());
            }else{
                FileCreation.createFile(workDir, relativeFilePath, b.getValue());
            }
        }
    }

    //找到tree下所有的blob 并记录在hashMap中
    public static HashMap<String,String> getAllBlob(String treePath) throws IOException {
        return getAllBlob(treePath, new ArrayList<String>(), new HashMap<String,String>());
    }

    private static HashMap<String,String> getAllBlob(
            String treePath,
            ArrayList<String> list,
            HashMap<String,String> fileContainer) throws IOException {

        Tree tree = Tree.deserialize(treePath);
        String treeValue = tree.getValue();
        String[] treeList = treeValue.split("\n");

        for(String listItem: treeList){
            String[] itemList = listItem.split("\\s+");
            if(itemList[1].equals("tree")){
                //recursion
                list.add(itemList[3]);
                getAllBlob(itemList[2], list, fileContainer);
            }else{// "blob"
                if(list.size() != 0 ){
                    String absPath = "";
                    for(String d:list){
                        absPath += (d + File.separator);
                    }
                    fileContainer.put(itemList[2], absPath+itemList[3]);
                }else{
                    fileContainer.put(itemList[2], itemList[3]);
                }
            }
        }
        return fileContainer;
    }
}
